package club.cearnach.security.filter;

import club.cearnach.core.property.VCodeProperties;
import club.cearnach.security.token.EmailVerificationCodeAuthenticationToken;
import club.cearnach.security.vcode.entity.VerificationCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author 阮胜
 * @date 2018/5/26 0:24
 */
@Slf4j
public class EmailCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    private final VCodeProperties vCodeProperties;
    public static final String GET = "GET";
    private static final String USERNAME = "username";
    private static final String TOKEN = "token";
    private String redisVCodeMapKey;
    private final RedisTemplate<String, Object> redisTemplate;

    public EmailCodeAuthenticationFilter(RedisTemplate<String, Object> redisTemplate, VCodeProperties vCodeProperties) {
        super(new AntPathRequestMatcher("/user/verify/login", "GET"));
        this.vCodeProperties = vCodeProperties;
        this.redisTemplate = redisTemplate;
        redisVCodeMapKey = vCodeProperties.getSessionKeyPrefix().concat("email");
    }


    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (!request.getMethod().equals(GET)) {
            throw new AuthenticationServiceException(
                    "认证方法不支持: " + request.getMethod());
        }

        String username = obtainUsername(request);
        String token = obtainToken(request);

        if (username == null) {
            username = "";
        }

        if (token == null) {
            token = "";
        }
        checkEmailVCode(username, token);
        username = username.trim();
        EmailVerificationCodeAuthenticationToken authRequest = new EmailVerificationCodeAuthenticationToken(
                username, token);
        setDetails(request, authRequest);
        return this.getAuthenticationManager().authenticate(authRequest);
    }

    /**
     * 校验邮箱验证码是否正确
     */
    private void checkEmailVCode(String account, String token) {
        VerificationCode verificationCode = (VerificationCode) redisTemplate.opsForHash().get(redisVCodeMapKey, account);
        //此处判断为空不能删除
        if (StringUtils.isEmpty(token)
                || verificationCode == null
                || StringUtils.isEmpty(verificationCode.getCode())
                || !token.equals(verificationCode.getCode())) {
            throw new AuthenticationServiceException("验证失败");
        }
        redisTemplate.opsForHash().delete(redisVCodeMapKey, account);
    }

    private void setDetails(HttpServletRequest request,
                            EmailVerificationCodeAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }

    private String obtainToken(HttpServletRequest request) {
        return request.getParameter(TOKEN);
    }

    private String obtainUsername(HttpServletRequest request) {
        return request.getParameter(USERNAME);
    }
}
